package com.novoda.runbuddy.activity;

import static com.novoda.runbuddy.RunBuddyApplication.TAG;

import java.util.ArrayList;
import java.util.List;

import android.content.Intent;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;
import com.google.android.maps.Projection;
import com.novoda.runbuddy.provider.RunBuddyCP;

public class RouteViewer extends MapActivity {

	private Cursor					routeCursor;
	private Cursor					routePointCursor;

	// UI objects and controller
	private MapController			mapController;
	private MapView					mapView;
	// Handler for the progress bar while we iterate through the list of geo
	// points
	final Handler					locationUIHandler			= new Handler();

	private static final int		INDEX_ROUTE_NAME			= 0;
	private static final int		CENTER_LATITUDE				= 1;
	private static final int		CENTER_LONGITUDE			= 2;
	private static final String[]	ROUTE_PROJECTION			= new String[] {
			RunBuddyCP.Routes.NAME,
			RunBuddyCP.Routes.LATITUDE,
			RunBuddyCP.Routes.LONGITUDE						};

	private static final int		INDEX_ROUTE_POINT_LATITUDE	= 0;
	private static final int		INDEX_ROUTE_POINT_LONGITUDE	= 1;
	private static final String[]	ROUTE_POINT_PROJECTION		= new String[] {
			RunBuddyCP.RoutePoints.LATITUDE,
			RunBuddyCP.RoutePoints.LONGITUDE					};

	/*
	 * To speed up things, we are using an array of ints for the latitude and
	 * longitude that we fill during the onCreate
	 */
	private int[]					latitudePoints;
	private int[]					longitudePoints;

	// Minimum & maximum latitude so we can span it
	// The latitude is clamped between -80 degrees and +80 degrees inclusive
	// thus we ensure that we go beyond that number
	private int						minLatitude					= (int) (+81 * 1E6);
	private int						maxLatitude					= (int) (-81 * 1E6);

	// Minimum & maximum longitude so we can span it
	// The longitude is clamped between -180 degrees and +180 degrees inclusive
	// thus we ensure that we go beyond that number
	private int						minLongitude				= (int) (+181 * 1E6);	;
	private int						maxLongitude				= (int) (-181 * 1E6);	;

	@Override
	protected void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		Intent intent = getIntent();
		//if (intent == null) {
			Log.e(TAG, "Can not display single route without a URI");
			Intent newIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("content://com.novoda.runbuddy/routes/60884"));
			setIntent(newIntent);
		//}

		routeCursor = managedQuery(intent.getData(), ROUTE_PROJECTION, null, null, null);

		fillLocationArrays(intent.getData());
		computeSpan();

		mapView = new MapView(this, "08DjdyEeCp1siuShuuFNmN1J4AZJDF0g89-4G5w");
		setContentView(mapView);
	}

	public void computeSpan() {
		for (int i = 0; i < latitudePoints.length; i++) {
			// Sets the minimum and maximum latitude so we can span and zoom
			minLatitude = (minLatitude > latitudePoints[i]) ? latitudePoints[i] : minLatitude;
			maxLatitude = (maxLatitude < latitudePoints[i]) ? latitudePoints[i] : maxLatitude;

			// Sets the minimum and maximum latitude so we can span and zoom
			minLongitude = (minLongitude > longitudePoints[i]) ? longitudePoints[i] : minLongitude;
			maxLongitude = (maxLongitude < longitudePoints[i]) ? longitudePoints[i] : maxLongitude;
		}
	}

	@Override
	protected void onStart() {
		super.onStart();
		GeoPoint center = null;

		if (routeCursor != null && routeCursor.moveToFirst()) {
			center = new GeoPoint((int) routeCursor.getLong(CENTER_LATITUDE), (int) routeCursor.getLong(CENTER_LONGITUDE));
		}

		mapController = mapView.getController();
		if (center != null)
			mapController.setCenter(center);

		mapView.getOverlays().add(new SingleRouteOverlay());
		// mapView.getController().setZoom(13);

		Log.i("RunBuddy", (maxLatitude - minLatitude) + " " + (maxLongitude - minLongitude));
		Log.i("RunByddy", "max latitude = " + maxLatitude);
		Log.i("RunByddy", "min latitude = " + minLatitude);

		Log.i("RunByddy", "max  = " + maxLongitude);
		Log.i("RunByddy", "min Longitude = " + minLongitude);

		Log.i("RunBuddy", "latitude" + (minLatitude + (maxLatitude - minLatitude)));
		Log.i("RunBuddy", "longitude" + (minLongitude + (maxLongitude - minLongitude)));

		mapView.getController().zoomToSpan((maxLatitude - minLatitude), (maxLongitude - minLongitude));
		mapView.getController().setCenter(
				new GeoPoint(minLatitude + (maxLatitude - minLatitude) / 2, minLongitude + (maxLongitude - minLongitude) / 2));

		// mapView.getController().setCenter(new GeoPoint(51368938, 1223601));
		// mapView.getController().setZoom(13);

	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
	}

	@Override
	protected void onPause() {
		super.onPause();
	}

	@Override
	protected void onResume() {
		super.onResume();
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	private void fillLocationArrays(Uri pointsUri) {
		Cursor cur = getContentResolver().query(pointsUri, ROUTE_POINT_PROJECTION, null, null, null);
		int size = cur.getCount();
		int currentPosition = 0;
		latitudePoints = new int[size];
		longitudePoints = new int[size];
		while (cur.moveToNext()) {
			currentPosition = cur.getPosition();
			latitudePoints[currentPosition] = (int) cur.getLong(INDEX_ROUTE_POINT_LATITUDE);
			longitudePoints[currentPosition] = (int) cur.getLong(INDEX_ROUTE_POINT_LONGITUDE);
		}
		cur.close();
	}

	protected void startLongRunningOperation() {

		// Fire off a thread to do some work that we shouldn't do directly in
		// the UI thread
		Thread t = new Thread() {
			public void run() {
				//mResults = doSomethingExpensive();
				locationUIHandler.post(mUpdateResults);
			}
		};
		t.start();
	}

	// Create runnable for posting
	final Runnable	mUpdateResults	= new Runnable() {
										public void run() {
											updateResultsInUi();
										}
									};

	private void updateResultsInUi() {
		// Back in the UI thread -- update our UI elements based on the data in
		// mResults
	}

	/*
	 * 
	 * Overlay for a single route
	 */
	private class SingleRouteOverlay extends Overlay {

		private int[]	points;

		private int		previousLatitude;
		private int		previousLongitude;

		public SingleRouteOverlay() {
			previousLatitude = latitudePoints[0];
			previousLongitude = longitudePoints[0];
		}

		@Override
		public void draw(Canvas canvas, MapView mapView, boolean shadow) {
			super.draw(canvas, mapView, shadow);

			Projection pro = mapView.getProjection();
			android.graphics.Point point = null;
			Point previousPoint = null;

			for (int i = 1; i < latitudePoints.length; i++) {
				previousPoint = pro.toPixels(new GeoPoint(previousLatitude, previousLongitude), null);
				previousLatitude = latitudePoints[i];
				previousLongitude = longitudePoints[i];

				point = pro.toPixels(new GeoPoint(latitudePoints[i], longitudePoints[i]), null);
				canvas.drawLine((float) previousPoint.x, (float) previousPoint.y, (float) point.x, (float) point.y,
						getRoutePathPainter());

			}
		}

		private Paint getRoutePathPainter() {
			Paint paint = new Paint();
			paint.setAntiAlias(true);
			paint.setARGB(80, 255, 0, 0);
			paint.setStrokeWidth(3);
			paint.setStyle(Paint.Style.FILL_AND_STROKE);
			return paint;
		}
	}

	/**
	 * This class will display all the checkpoints on the map. The checkpoints
	 * are the unit against which a runner will be able to check his timings. It
	 * has a user representation of the route (compared to routePoint which is a
	 * computing representation of the route).
	 * 
	 * It extends {@link ItemizedOverlay} and takes a {@link java.util.List} of
	 * checkpoints.
	 * 
	 * If compared to routePoints build up, it uses a list instead of an array
	 * of ints. The reason being that we have much less checkpoints then
	 * routepoints.
	 * 
	 */
	public class CheckPointItemizedOverlay extends ItemizedOverlay<CheckPointOverlayItem> {

		private List<CheckPointOverlayItem>	checkpoints	= null;

		private Drawable					balloon		= null;

		/**
		 * Default Constructor - not in use
		 * 
		 * @param defaultMarker
		 */
		public CheckPointItemizedOverlay(Drawable defaultMarker) {
			super(defaultMarker);
		}

		/**
		 * 
		 * The Overlay should not access directly the CP. Thus the necessary
		 * data should be set within the constructor. In this case we receive a
		 * list of checkpoints which contains the lat/long. If you want to have
		 * further information about the checkpoint - against run times for
		 * instance - you should use {@link #CheckPointItemizedOverlay(Drawable,
		 * Cursor, List<View> )}
		 * 
		 * @param defaultMarker
		 *            The default marker
		 * @param checkpoints
		 *            The list of all checkpoints for this route as a cursor
		 */
		public CheckPointItemizedOverlay(Drawable defaultMarker, List<CheckPointOverlayItem> checkpoints) {
			super(defaultMarker);
			balloon = defaultMarker;
			this.checkpoints = new ArrayList<CheckPointOverlayItem>(checkpoints);
		}

		@Override
		protected CheckPointOverlayItem createItem(int i) {
			return checkpoints.get(i);
		}

		@Override
		public int size() {
			return checkpoints.size();
		}

		@Override
		public void draw(Canvas canvas, MapView mapView, boolean shadow) {
			super.draw(canvas, mapView, shadow);
			boundCenterBottom(balloon);
		}

	}

	public class CheckPointOverlayItem extends OverlayItem {

		public CheckPointOverlayItem(GeoPoint point, String title, String snippet) {
			super(point, title, snippet);
		}

	}
}
